package com.pie4cloud.pie.job.executor.service;

import com.cronutils.model.definition.CronDefinition;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.time.ExecutionTime;
import com.cronutils.parser.CronParser;
import com.pie4cloud.pie.common.core.util.R;
import com.pie4cloud.pie.dmz.entty.TransferInfo;
import com.pie4cloud.pie.dmz.entty.enums.MsgTypeEnum;
import com.pie4cloud.pie.job.api.entity.DispenseCongfigEntity;

import com.pie4cloud.pie.job.api.entity.ConfigFtp;
import com.pie4cloud.pie.job.api.vo.MybatisVo;
import com.pie4cloud.pie.job.api.vo.TimeExpressionVo;
import com.pie4cloud.pie.job.api.vo.TimeLevelVo;
import com.pie4cloud.pie.job.api.vo.UploadVo;
import com.pie4cloud.pie.job.executor.dto.StatisticalRequest;
import com.pie4cloud.pie.job.executor.enums.StatisticalType;
import com.pie4cloud.pie.job.executor.mapper.MybatisQueryMapper;
import com.pie4cloud.pie.job.executor.services.StatisticalService;
import com.pie4cloud.pie.job.executor.util.CronExpression;
import com.pie4cloud.pie.job.executor.utils.DateExpressionEngine;
import com.pie4cloud.pie.job.executor.utils.FtpClient;
import com.pie4cloud.pie.job.executor.utils.ftp.FtpProcessor;
import com.pie4cloud.pie.job.executor.utils.ftp.FtpUtil;
import com.pie4cloud.pie.job.executor.utils.netty.NettyUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.cronutils.model.CronType.QUARTZ;

@Slf4j
@Service
public class DispenseToolService {
	protected  final String REGEX = "(\\#\\{(.*?)\\})";
	protected  final Pattern PATTERN = Pattern.compile(REGEX);
	@Autowired
	private MybatisQueryMapper mybatisQueryMapper;
	@Autowired
	private NettyUtil nettyUtil;
	@Autowired
	private StatisticalService statisticalService;
	@Autowired
	private FtpUtil ftpUtil;
	/**
	 * 计算历史时次
	 * @param dispenseCongfigEntity
	 * @param timeLevelVos
	 * @param result
	 */
	public void computingTime(DispenseCongfigEntity dispenseCongfigEntity, List<TimeLevelVo> timeLevelVos, R<String> result) {
		long startTime=dispenseCongfigEntity.getDispenseHistory().getHistoryStartTime().getTime();
        long endTime=dispenseCongfigEntity.getDispenseHistory().getHistoryEndTime().getTime();
        while (startTime<endTime){
             long timeKey=this.computingSameDay(startTime);
             TimeLevelVo timeLevelVo=new TimeLevelVo();
			 timeLevelVo.setStartTime(startTime);
			 timeLevelVo.setEndTime(this.computingNextDay(timeKey));
			 timeLevelVo.setTimeKey(timeKey);
			 startTime=timeLevelVo.getEndTime();
			 if(timeLevelVo.getEndTime()>endTime){
				 timeLevelVo.setEndTime(endTime);
			 }
			 timeLevelVos.add(timeLevelVo);
		}

	}

	/**
	 * 计算当天0点
	 * @param time
	 * @return
	 */
	public long computingSameDay(long time){
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date(time));
		calendar.set(Calendar.SECOND,0); //这是将当天的【秒】设置为0
		calendar.set(Calendar.MINUTE,0); //这是将当天的【分】设置为0
		calendar.set(Calendar.HOUR_OF_DAY,0); //这是将当天的【时】设置为0
		return calendar.getTimeInMillis();
	}

	/**
	 * 计算下一天0点
	 * @param time
	 * @return
	 */
	public long computingNextDay(long time){
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date(time));
		calendar.set(Calendar.SECOND,0); //这是将当天的【秒】设置为0
		calendar.set(Calendar.MINUTE,0); //这是将当天的【分】设置为0
		calendar.set(Calendar.HOUR_OF_DAY,0); //这是将当天的【时】设置为0
		calendar.add(Calendar.DATE,1); //当前日期加一
		return calendar.getTimeInMillis();
	}

	/**
	 * 提取文件时间表达式
	 * @param filePath
	 * @param timeLevelVo
	 * @return
	 */
	public void drawTimeExpression(String filePath, TimeLevelVo timeLevelVo, TimeExpressionVo timeExpressionVo){
		filePath = DateExpressionEngine.formatDateExpression(filePath,timeLevelVo.getTimeKey());
		Matcher m = PATTERN.matcher(filePath);
		while (m.find()) {
			String expression = m.group(2);
			String replaceMent = expression.split(",")[0].replaceAll("[ymdhsYMDHS]", "\\\\d");
			filePath = filePath.replace("#{" + expression + "}", replaceMent);
			timeExpressionVo.setFormat(expression);
			timeExpressionVo.setExpression(replaceMent);
		}
		timeExpressionVo.setIndex(this.getIndex(filePath,timeExpressionVo.getExpression()));
		timeExpressionVo.setSourceFileName(filePath);
	}

	/**
	 * 实时提取文件时间表达式
	 * @param filePath
	 * @return
	 */
	public void drawTimeRealExpression(String filePath,TimeExpressionVo timeExpressionVo){
		filePath = DateExpressionEngine.formatDateExpression(filePath);
		Matcher m = PATTERN.matcher(filePath);
		while (m.find()) {
			String expression = m.group(2);
			String replaceMent = expression.split(",")[0].replaceAll("[ymdhsYMDHS]", "\\\\d");
			filePath = filePath.replace("#{" + expression + "}", replaceMent);
			timeExpressionVo.setFormat(expression);
			timeExpressionVo.setExpression(replaceMent);
		}
		timeExpressionVo.setIndex(this.getIndex(filePath,timeExpressionVo.getExpression()));
		timeExpressionVo.setSourceFileName(filePath);
	}

	/**
	 * 获取表达式索引位置
	 * @param str
	 * @param value
	 * @return
	 */
	public int getIndex(String str,String value){
		String regex = "(?<=\\()(.+?)(?=\\))";
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(str);
		//System.out.println("replace: "+str.replaceAll(regex, "O"));
		int k=-1;
		while (matcher.find()) {
			for (int j = 1; j <= matcher.groupCount(); j++){
				k++;
				String timeStr = matcher.group(j);
				if(timeStr.equals(value)){
					return k;
				}
			}
		}
		return k;
	};

	/**
	 * 提取${}
	 * @param str
	 * @return
	 */
	public List<String> drawBrace(String str){
		List<String> ls=new ArrayList<String>();
		String regex = "(\\$\\{(.*?)\\})";
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(str);
		while (matcher.find()) {
			for (int j = 1; j <= matcher.groupCount(); j++){
				String timeStr = matcher.group(j);
				ls.add(timeStr);
			}
		}
		return ls;
	};

	/**
	 * 提取正则
	 * @param managers
	 * @param res
	 * @return
	 */
	public  List<String> drawRegular(String managers,String res){
		List<String> ls=new ArrayList<String>();
		Pattern pattern=Pattern.compile(res);
		Matcher matcher = pattern.matcher(managers);
		while(matcher.find())
			for (int j = 1; j <= matcher.groupCount(); j++){
				String timeStr = matcher.group(j);
				ls.add(timeStr);
			}
		return ls;
	}

	/**
	 * 获取下一个时次
	 * @param cron
	 * @param date
	 * @return
	 */
	public  Date getNextTriggerTime(String cron,Date date){

		try {
			Date nextValidTime =new CronExpression(cron).getNextValidTimeAfter(date);
			return nextValidTime;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
		/*if(StringUtils.isEmpty(cron)){
			return date;
		}
		CronExpression cronExpression =CronExpression.parse(cron);
		ZoneId zoneId = ZoneId.systemDefault();
		LocalDateTime time1 = cronExpression.next(LocalDateTime.ofInstant(date.toInstant(), zoneId));//下次执行时间
		return Date.from(time1.atZone(zoneId).toInstant());*/
	}
	public  Date getLastTriggerTime(String cron,Date date){
		final ZoneId id = ZoneId.systemDefault();
		ZonedDateTime now = ZonedDateTime.ofInstant(date.toInstant(), id);
		CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(QUARTZ);
		CronParser parser = new CronParser(cronDefinition);
		ExecutionTime executionTime = ExecutionTime.forCron(parser.parse(cron));
		try {
			Optional<ZonedDateTime> lastExecution = executionTime.lastExecution(now);
            return  Date.from(lastExecution.get().toInstant());
		} catch (Exception e) {
			e.printStackTrace();
		}
		/*try {
			Date time1 =new CronExpression(cron).getNextValidTimeAfter(date);
			Date time2 =new CronExpression(cron).getNextValidTimeAfter(time1);
			Date time3 =new CronExpression(cron).getNextValidTimeAfter(time2);
			long time=time1.getTime()-(time3.getTime()-time2.getTime());
			if(time==date.getTime()){
				 time=time-(time3.getTime()-time2.getTime());
			}
			return new Date(time);
		} catch (ParseException e) {
			e.printStackTrace();
		}*/
		return null;
	}

	/**
	 * 提取括号
	 * @param str
	 * @return
	 */
	public List<String> drawBracket(String str){
		List<String> ls=new ArrayList<String>();
		String regex = "(?<=\\()(.+?)(?=\\))";
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(str);
		//System.out.println("replace: "+str.replaceAll(regex, "O"));
		while (matcher.find()) {
			for (int j = 1; j <= matcher.groupCount(); j++){
				String timeStr = matcher.group(j);
				ls.add(timeStr);
			}
		}
		return ls;
	};

	/**
	 * 替换(|) 生成多个目录
	 * @param dourceDirectory
	 * @param timeLevelVo
	 * @return
	 */
	public  List<String> drawFolder(String dourceDirectory,TimeLevelVo timeLevelVo){
		List<String> folderBracket=this.drawBracket(dourceDirectory);
		List<String> folderList=new ArrayList<>();
		String sourceDirectory=DateExpressionEngine.formatDateExpression(dourceDirectory,timeLevelVo.getTimeKey());
		folderList.add(sourceDirectory);
		for(int i=0;i<folderBracket.size();i++){
			String[] folderChild=folderBracket.get(i).split("\\|");
			List<String> folderChildList=new ArrayList<>();
			for(int j=0;j<folderChild.length;j++){
				for(int k=0;k<folderList.size();k++){
					folderChildList.add(folderList.get(k).replaceAll("\\("+StringUtils.join(folderChild, "\\|")+"\\)",folderChild[j]));
				}
			}
			folderList.clear();
			folderList.addAll(folderChildList);
		}
		return folderList;
	}

	/**
	 * 正则过滤文件列表
	 * @param dispenseCongfigEntity
	 * @param timeLevelVo
	 * @param timeExpressionVo
	 * @return
	 */
	public List<Path> getPathList(DispenseCongfigEntity dispenseCongfigEntity, TimeLevelVo timeLevelVo, TimeExpressionVo timeExpressionVo){
		if(!new File(timeExpressionVo.getSourceDirectory()).exists()){
			return new ArrayList<>();
		}
		Path pathSource = Paths.get(timeExpressionVo.getSourceDirectory());
		String sourceFileName=dispenseCongfigEntity.getSourceFileName();
		List<Path> paths = null;
		try {
			this.drawTimeExpression(sourceFileName,timeLevelVo,timeExpressionVo);
			Pattern pattern = Pattern.compile(timeExpressionVo.getSourceFileName());
			paths = Files.walk(pathSource).filter(p -> {
				//如果不是普通的文件，则过滤掉
				if(!Files.isRegularFile(p)) {
					return false;
				}
				File file = p.toFile();
				String path=file.getAbsolutePath().replaceAll("\\\\","/");
				Matcher matcher = pattern.matcher(path);
				return matcher.find();
			}).collect(Collectors.toList());
		}  catch (IOException e) {
			e.printStackTrace();
		}
		if(paths==null){
			paths=new ArrayList<>();
		}
		return paths;

	}

	/**
	 * 预备扫描目录
	 * @param dispenseCongfigEntity
	 * @param timeLevelVo
	 * @param result
	 */
	public void scanPreFolder(DispenseCongfigEntity dispenseCongfigEntity,TimeLevelVo timeLevelVo,R<String> result){
		List<String> folderList=this.drawFolder(dispenseCongfigEntity.getSourceDirectory(),timeLevelVo);
		for(String folder:folderList){
			TimeExpressionVo timeExpressionVo=new TimeExpressionVo();
			timeExpressionVo.setSourceDirectory(folder);
			this.scanFolder(dispenseCongfigEntity,timeLevelVo,timeExpressionVo);
		}
	}

	/**
	 * 扫描数据库
	 * @param dispenseCongfigEntity
	 * @param timeLevelVo
	 * @param result
	 */
	public void scanDataBase(DispenseCongfigEntity dispenseCongfigEntity,TimeLevelVo timeLevelVo,R<String> result){
		MybatisVo mybatisVo = new MybatisVo();
		mybatisVo.setDdataId("'" + dispenseCongfigEntity.getDdataId() + "'");
		mybatisVo.setTableName(dispenseCongfigEntity.getDispenseTiming().getTableName());
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		mybatisVo.setBeginTime("'" + simpleDateFormat.format(timeLevelVo.getStartTime()) + "'");
		mybatisVo.setEndTime("'" + simpleDateFormat.format(timeLevelVo.getEndTime()) + "'");
		List<Map<String, Object>> maps = mybatisQueryMapper.selectFidbInfo(mybatisVo, "ht_fidb");
		if(null!=maps){
			for(Map<String,Object> map:maps){
				TimeExpressionVo timeExpressionVo=new TimeExpressionVo();
				this.drawTimeExpression(dispenseCongfigEntity.getSourceFileName(),timeLevelVo,timeExpressionVo);
				String filePath=(String) map.get("D_STORAGE_SITE");
				Date ddataTime= (Date) map.get("D_DATETIME");
				UploadVo uploadVo=new UploadVo();
				uploadVo.setDdataTime(ddataTime.getTime());
				this.rulesDirectoryAndFileName(dispenseCongfigEntity,new File(filePath),timeLevelVo,timeExpressionVo,uploadVo);
			}
		}
	}

	/**
	 * 扫描目录
	 * @param dispenseCongfigEntity
	 * @param timeLevelVo
	 * @param timeExpressionVo
	 */
	public void scanFolder(DispenseCongfigEntity dispenseCongfigEntity,TimeLevelVo timeLevelVo,TimeExpressionVo timeExpressionVo){
		List<Path> paths=this.getPathList(dispenseCongfigEntity,timeLevelVo,timeExpressionVo);
		for(Path path:paths){
			UploadVo uploadVo=new UploadVo();
			this.rulesDirectoryAndFileName(dispenseCongfigEntity,path.toFile(),timeLevelVo,timeExpressionVo,uploadVo);
		}
	}

	/**
	 * 规整目标路径和文件名
	 * @param dispenseCongfigEntity
	 * @param file
	 * @param timeLevelVo
	 * @param timeExpressionVo
	 * @param uploadVo
	 */
	public void rulesDirectoryAndFileName(DispenseCongfigEntity dispenseCongfigEntity,File file,TimeLevelVo timeLevelVo,TimeExpressionVo timeExpressionVo,UploadVo uploadVo){
		String filePath=file.getPath().replaceAll("\\\\","/");
		List<String> regulars=this.drawRegular(filePath,timeExpressionVo.getSourceFileName());
		if(0==uploadVo.getDdataTime()){
			this.getDateTime(regulars,timeLevelVo,timeExpressionVo,uploadVo);
			if(!uploadVo.isInclude()){
				return;
			}
		}
		List<String> brackets=this.drawBracket(dispenseCongfigEntity.getTargetDirectory());
		String targetDirectory=DateExpressionEngine.formatDateExpression(dispenseCongfigEntity.getTargetDirectory(),uploadVo.getDdataTime());
		String fileName=file.getName();
		String targetFileName=DateExpressionEngine.formatDateExpression(dispenseCongfigEntity.getTargetFileName(),uploadVo.getDdataTime());
		if(StringUtils.isNotEmpty(targetFileName)){
			fileName=targetFileName;
		}
		if(regulars.size()>0&&brackets.size()>0){
			for(int i=0;i<regulars.size();i++){
				targetDirectory=targetDirectory.replaceAll("\\("+i+"\\)",regulars.get(i));
				if(StringUtils.isNotEmpty(targetFileName)){
					fileName=fileName.replaceAll("\\("+i+"\\)",regulars.get(i));
				}
			}

		}else {
			targetDirectory=filePath.substring(0,filePath.lastIndexOf("/")).replaceAll(dispenseCongfigEntity.getSourceDirectory(),dispenseCongfigEntity.getTargetDirectory());
		}
		uploadVo.setDstorageSite(filePath);
		uploadVo.setTargetDirectory(targetDirectory);
		uploadVo.setFileName(fileName);
		uploadVo.setDmzPath(dispenseCongfigEntity.getDmzPath());
		uploadVo.setIsDmz(dispenseCongfigEntity.getIsDmz());
		this.uploadFile(dispenseCongfigEntity.getConfigFtp(),uploadVo);
	}

	/**
	 * 获取资料时间
	 * @param regulars
	 * @param timeLevelVo
	 * @param timeExpressionVo
	 * @param uploadVo
	 */
	public void getDateTime(List<String> regulars, TimeLevelVo timeLevelVo, TimeExpressionVo timeExpressionVo, UploadVo uploadVo){
		long datatime=timeLevelVo.getTimeKey();
		uploadVo.setInclude(true);
		if(StringUtils.isNotEmpty(timeExpressionVo.getExpression())){
			SimpleDateFormat simpleDateFormat=new SimpleDateFormat(timeExpressionVo.getFormat());
			try {
				datatime=simpleDateFormat.parse(regulars.get(timeExpressionVo.getIndex())).getTime();
				if(!timeLevelVo.isRealTime()&&(datatime<timeLevelVo.getStartTime()||datatime>=timeLevelVo.getEndTime())){
                   uploadVo.setInclude(false);
				}
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		uploadVo.setDdataTime(datatime);
	}

	/**
	 * 上传文件
	 * @param configFtp
	 * @param uploadVo
	 */
	public void uploadFile(ConfigFtp configFtp,UploadVo uploadVo){

			StatisticalRequest statisticalRequest = new StatisticalRequest();
			statisticalRequest.setStatisticalType(StatisticalType.GATHER);
			statisticalRequest.setPlaybillId(1l);
			statisticalRequest.setFilePath(uploadVo.getFileName() + "/" + uploadVo.getFileName());
			statisticalRequest.setWhenTime(uploadVo.getTimeKey());
			statisticalRequest.setFtpId(configFtp.getFtpId());
			if (statisticalService.check(statisticalRequest)) {
				if (1 == uploadVo.getIsDmz()) {
					this.uploadDmz(configFtp, uploadVo);
					//this.downDmz(configFtp, uploadVo);
				} else {
					//FtpClient ftpClient = new FtpClient().getFtpClient(configFtp);
					File file = new File(uploadVo.getDstorageSite());
					if (!file.exists()) {
						log.error("{}文件不存在", uploadVo.getDstorageSite());
					}
					FtpProcessor ftpProcessor=ftpUtil.getFtpProcessor(configFtp);
					ftpProcessor.uploadFile(uploadVo.getDstorageSite(),uploadVo.getTargetDirectory(),uploadVo.getFileName());
				}
			}


	}

	public void uploadDmz(ConfigFtp configFtp,UploadVo uploadVo){
		TransferInfo transferInfo=new TransferInfo();
		transferInfo.setFtpId(configFtp.getFtpId());
		transferInfo.setFtpAddress(configFtp.getFtpAddress());
		transferInfo.setFtpUserName(configFtp.getFtpUserName());
		transferInfo.setFtpPort(configFtp.getFtpPort());
		transferInfo.setFtpPwd(configFtp.getFtpPwd());
		transferInfo.setFileName(new File(uploadVo.getDstorageSite()).getName());
		transferInfo.setFilePath(uploadVo.getDstorageSite());
		transferInfo.setTargetFileName(uploadVo.getFileName());
		transferInfo.setDmzPath(uploadVo.getDmzPath());
		transferInfo.setTargetPath(uploadVo.getTargetDirectory());
		transferInfo.setMsgTypeEnum(MsgTypeEnum.UPLOAD);
		try {
			Object result=nettyUtil.send(transferInfo);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void downDmz(ConfigFtp configFtp,UploadVo uploadVo){
		TransferInfo transferInfo=new TransferInfo();
		transferInfo.setFileName(new File(uploadVo.getDstorageSite()).getName());
		transferInfo.setFilePath(uploadVo.getDstorageSite());
		transferInfo.setTargetFileName(uploadVo.getFileName());
		transferInfo.setDmzPath("D:\\CMADAAS\\DATA\\DOWN");
		transferInfo.setTargetPath("D:\\CMADAAS\\DATA\\TARGET");
		transferInfo.setMsgTypeEnum(MsgTypeEnum.DOWNlOAD);
		try {
			Object result=nettyUtil.send(transferInfo);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
